<?php

namespace App\Library\Logic;

use App\Library\Requests\Base\BasePageRequest;
use App\Library\Requests\Invite\InviteOrderPageRequest;
use App\Library\Requests\ProductOrder\ProductOrderCreateRequest;
use App\Library\Requests\ProductOrder\ProductOrderPageRequest;
use App\Library\Requests\ProductOrder\ProductOrderSellerPageRequest;
use App\Library\Requests\ProductOrder\ProductOrderUserPageRequest;
use App\Models\ShopOrderModel;
use App\Models\SystemConfigModel;
use Exception;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;

class ShopOrderLogic extends ShopOrderModel
{
    /**
     * add
     * @param ProductOrderCreateRequest $request
     * @param array $data
     * @param Model $address
     * @param Model|null $couponUserRelated
     * @return Model
     * @throws Exception
     */
    public function add(ProductOrderCreateRequest $request, array $data, Model $address, ?Model $couponUserRelated): Model
    {
        if (!is_null($couponUserRelated)) {
            $data['price'] = bcsub($data['price'], $couponUserRelated->coupon->amount, 2);
            if ((float)$data['price'] <= 0) $data['price'] = 0.01;
        }
        $config = SystemConfigModel::query()->where('id', 1)->value('content');
        if ($data['feed'] === 1 && in_array($address->province_id, $config['costArea']['area'])) {
            $freight = cache('systemConfig:1')['costArea']['feed'];
            $data['price'] = bcadd($data['price'], $freight, 2);
        }

        $array = [
            'buyUserId' => USERID,
            'payPrice' => $data['price'],
            'orderTotalPrice' => $data['price'],
            'orderTotalRealPrice' => $data['price'],
            'orderContacts' => is_null($address) ? null : $address->contacts,
            'orderContactsMobile' => is_null($address) ? null : $address->mobile,
            'orderContactsAddress' => is_null($address) ? null : $address->full_address,
            'orderNote' => $request->remark,
            'sellUserId' => $data['sellerId'],
            'inviteCode' => $request->inviteCode,
            'orderProductNum' => $data['quantity'],
            'orderAddTime' => time(),
            'isCart' => $request->isCart,
            'couponDiscount' => $couponUserRelated->coupon->amount ?? 0,
        ];
        $order = self::insertForModel($array);
        mq('productOrderCancel', $order->serial_number, (int)__('base.cancelTtl'));
        ShopOrderExtLogic::add($order, $freight ?? 0, $address->id, $couponUserRelated->id ?? 0);
        ShopOrderGoodsLogic::add($data['data'], $order->order_id);
        CartLogic::clearBySpecIds($request);
        ProductSpecLogic::decStock($data['data']);
        return $order;
    }

    /**
     * paySuccess
     * @param int $id
     * @param string $payAt
     * @return int
     */
    public static function paySuccess(int $id, string $payAt): int
    {
        $int = self::query()->where(humpToLine(['orderId' => $id, 'orderStatus' => 2, 'orderPayTime' => 0]))
            ->update(humpToLine([
                'orderStatus' => 1,
                'orderBuyStatus' => 1,
                'orderSellStatus' => 1,
                'orderPayTime' => strtotime($payAt),
            ]));
        if ($int === 1) {
            DB::table('shop_order_ext')->where('order_id', $id)->update([
                'status' => 2,
                'status_pay' => 1,
                'pay_at' => $payAt,
            ]);
        }
        return $int;
    }

    /**
     * paging
     * @param ProductOrderPageRequest $request
     * @return array
     */
    public function paging(ProductOrderPageRequest $request): array
    {
        $builder = $this->newQuery()->leftJoin('shop_order_ext', 'shop_order_ext.order_id', 'shop_order.order_id')
            ->whereNull('shop_order_ext.system_at')->where('shop_order.buy_user_id', '>', 0);
        if ((int)$request['shopOrderExt.status'] === 7) {
            $builder->whereIn('status_after_sale', [1, 2])->whereNull('refund_at')->with('orderReturn');
            $request->offsetUnset('shopOrderExt.status');
        } else if ((int)$request['shopOrderExt.status'] === 8) {
            $builder->leftJoin('shop_order_return', 'shop_order_return.order_id', 'shop_order.order_id')
                ->where('or_buy_status', 4)->whereNull('refund_at')->with('orderReturn');
            $request->offsetUnset('shopOrderExt.status');
        }
        if (!is_null($request->userMobile)) {
            $builder->leftJoin('shop_user', 'shop_user.user_id', 'shop_order_ext.user_id')
                ->where('shop_user.user_mobile', $request->userMobile);
            $request->offsetUnset('userMobile');
        }
        if (!is_null($request->expressNo)) {
            $builder->leftJoin('logistics', function ($join) {
                $join->on('logistics.object_id', 'shop_order.order_id')->where('logistics.type', 0)
                    ->where('logistics.object_type', 1);
            })->where('logistics.express_no', $request->expressNo);
            $request->offsetUnset('expressNo');
        }
        if (!is_null($request->productId)) {
            $builder->leftJoin('shop_order_goods', 'shop_order_goods.order_id', 'shop_order.order_id')
                ->where('shop_order_goods.product_id', $request->productId);
            $request->offsetUnset('productId');
        }
        $paginate = helpBuilder($builder->with(['goods', 'packages', 'ext']), $request)
            ->groupBy('shop_order.order_id')->orderByDesc('shop_order.order_id')
            ->paginate($request->limit, array_merge($this->hColumns(), [
                'status',
                'order_contacts',
                'order_contacts_mobile',
                'order_contacts_address',
                'seller_remark',
                'order_note',
                'freight',
            ]));
        return array_merge(paginate($paginate), ['tj' => ShopOrderExtLogic::tj()]);
    }

    /**
     * userPaging
     * @param ProductOrderUserPageRequest $request
     * @return LengthAwarePaginator
     */
    public function userPaging(ProductOrderUserPageRequest $request): LengthAwarePaginator
    {
        $builder = $this->newQuery()
            ->leftJoin('shop_order_ext', 'shop_order_ext.order_id', 'shop_order.order_id')
            ->where('user_id', USERID)
            ->where('status_cancel', 0)
            ->where('buy_user_id', '>', 0);
        if (!isset($request->status)) {
            $builder = $builder->whereIn('shop_order_ext.status_after_sale', [0, 3, 4]);
        }
        $builder = $builder->whereNull('user_deleted_at')
            ->orderByDesc('shop_order.created_at');
        $this->hKeyword($request, $builder);
        return helpBuilder($builder->with(['ext', 'goods', 'packages']), $request)
            ->paginate($request->limit, $this->hColumns());
    }

    /**
     * sellerPaging
     * @param ProductOrderSellerPageRequest $request
     * @return array
     */
    public function sellerPaging(ProductOrderSellerPageRequest $request): array
    {
        $builder = $this->newQuery()->leftJoin('shop_order_ext', 'shop_order_ext.order_id', 'shop_order.order_id')
            ->where('status_cancel', 0)->where('buy_user_id', '>', 0)->whereNull('system_at')
            ->whereNull('seller_deleted_at')->orderByDesc('shop_order.created_at');
        $this->hKeyword($request, $builder);
        $this->dateFilter($request, $builder, 'orderAddTime');
        if (USERID === MAINID || POWER === 1) $builder->where('shop_order_ext.seller_id', MAINID);
        else $builder->where('seller_id', USERID);
        $builder->where([['shop_order_ext.status_after_sale', '!=', 2]]);
        return paginate(helpBuilder($builder->with(['ext', 'goods', 'packages']), $request)
            ->paginate($request->limit, array_merge($this->hColumns(), [
                'order_contacts',
                'order_contacts_mobile',
                'order_contacts_address',
            ])));
    }

    /**
     * userDetail
     * @param string $serialNumber
     * @param array $columns
     * @return Model|null
     */
    public function userDetail(string $serialNumber, array $columns = []): ?Model
    {
        return self::findByWhere(['serialNumber' => $serialNumber, 'buyUserId' => USERID, 'orderIsDelete' => 2], null,
            array_merge($columns, $this->hColumns(), [
                'order_contacts',
                'order_contacts_mobile',
                'order_contacts_address',
                'order_note',
                'discount',
                'active_discount',
                'plug',
            ]));
    }

    /**
     * sellerDetail
     * @param string $serialNumber
     * @param array $columns
     * @return Model|null
     */
    public function sellerDetail(string $serialNumber, array $columns = []): ?Model
    {
        $builder = self::query()->leftJoin('shop_user', 'shop_user.user_id', 'shop_order.buy_user_id')
            ->where(humpToLine(['serialNumber' => $serialNumber, 'orderIsDelete' => 2]));
        if (USERID === MAINID || POWER === 1) $builder->where('sell_user_id', MAINID);
        else $builder->where('sell_user_id', USERID);
        return $builder->first(humpToLine(array_merge($columns, $this->hColumns(), [
            'orderContacts',
            'orderContactsMobile',
            'orderContactsAddress',
            'orderNote',
            'userMobile',
            'userNickname',
            'discount',
            'active_discount',
        ])));
    }

    /**
     * getCreateData
     * @param Collection|array $specList
     * @param ProductOrderCreateRequest $request
     * @return Model|null
     */
    public static function getCreateData(Collection|array $specList, ProductOrderCreateRequest $request): array|string
    {
        $totalPrice = $number = 0;
        $data = $userIds = [];
        $agent = cache()->has('agent:' . USERID);
        foreach ($specList as $v) {
            if (!$agent && $v->is_agent) return '当前用户没有权限购买';
            $quantity = $request->specArr[$v->ps_id];
            if ($quantity > $v->ps_usable_stock) return "商品可用库存不足($v->ps_usable_stock)";
            else if ($v->ps_buy_limit > 0) {
                if ($quantity > $v->ps_buy_limit) return "购买数量($quantity)大于限购数量($v->ps_buy_limit)";
                $count = ShopOrderGoodsLogic::limitBuy($v->ps_id);
                if ($quantity + $count > $v->ps_buy_limit) {
                    return "购买数量($quantity)+已经购买的数量($count)大于限购数量($v->ps_buy_limit)";
                } else {
                    if ($quantity + $count > $v->ps_usable_stock) {
                        return "购买数量($quantity)+已经购买的数量($count)大于可用库存($v->ps_usable_stock)";
                    }
                }
            }
            $start = now()->gt(now()->parse($v->startAt));
            $end = now()->lt(now()->parse($v->endAt));
            $v->special_price = $start && $end && (float)$v->ps_price > 0 ? $v->ps_price : $v->ps_price_underlined;
            $totalPrice += $v->special_price * $request->specArr[$v->ps_id];
            $userIds[] = $v->user_id;
            $data[] = ShopOrderGoodsLogic::createData($quantity, $v);
            $number += $quantity;
            if ($v->product_category_id_1 === 2) $feed = 1;
        }
        $userIds = array_unique($userIds);
        return [
            'feed' => $feed ?? 0,
            'price' => (float)$totalPrice,
            'data' => $data ?? [],
            'quantity' => $number,
            'sellerId' => count($userIds) === 1 ? $userIds[0] : 0,
            'agent' => $agent,
        ];
    }

    /**
     * cancel
     * @param string $serialNumber
     * @param int $time
     * @return void
     */
    public static function cancel(string $serialNumber, int $time = PHP_INT_MAX): void
    {
        $data = [
            'orderBuyStatus' => 3,
            'orderSellStatus' => 3,
            'orderCancelSn' => 'CN' . serialNumber() . random(4, 1),
            'orderCancelTime' => time(),
        ];
        $shopOrder = ShopOrderLogic::findBySerialNumber($serialNumber, ['plug', 'plug2']);
        $bool = !is_null($shopOrder) && ($shopOrder->plug > 0 || $shopOrder->plug2 > 0);
        $where = [
            'serialNumber' => $serialNumber,
            'orderIsDelete' => 2,
            'orderBuyStatus' => 2,
            'orderStatus' => 2,
            'orderAddTime' => ['<=', $time - (int)__('base.' . ($bool ? 'plugCancelTtl' : 'cancelTtl'))],
            'orderType' => 1,
            'orderCancelTime' => 0,
        ];
        if (self::updateByWhere($data, $where) === 1) {
            $orderId = self::valueByWhere('serialNumber', $serialNumber, 'orderId');
            ShopOrderExtLogic::updateByWhere([
                'status' => 0,
                'statusCancel' => 1,
                'statusInventory' => 0,
                'cancelAt' => now(),
            ], 'orderId', $orderId);
            ProductSpecLogic::incStock($orderId);
            $couponUserRelatedId = ShopOrderExtLogic::valueByWhere('orderId', $orderId, 'couponUserRelatedId');
            if (!is_null($couponUserRelatedId)) {
                $couponUserRelated = CouponUserRelatedLogic::detail($couponUserRelatedId);
                if (!is_null($couponUserRelated)) {
                    $couponUserRelated->status = 0;
                    $couponUserRelated->save();
                    if (!is_null($couponUserRelated->coupon)) {
                        $couponUserRelated->coupon->use_count--;
                        $couponUserRelated->coupon->save();
                    }
                }
            }
        }
    }

    /**
     * 统计商品购买总数
     * @param int $productId
     * @param int $psId
     * @return int
     */
    public static function countBoughtProduct(int $productId, int $psId): int
    {
        return self::query()->where([
            'buy_user_id' => USERID,
            'order_buy_status' => 3,
            'ps_id' => $psId,
            'product_id' => $productId,
        ])->count();
    }

    /**
     * 订单状态统计
     * @param int $type
     * @param int $userId
     * @return Model|Builder|null
     */
    public function userOrderTj(int $type = 0, int $userId = 0): Model|Builder|null
    {
        $builder = self::query()
            ->leftJoin(
                'shop_order_ext',
                'shop_order_ext.order_id',
                'shop_order.order_id'
            );
        if ($type === 0) {
            $builder->where('user_id', USERID)
                ->whereNull('user_deleted_at');
        } else {
            $builder->whereNull('seller_deleted_at')
                ->where('seller_id', $userId)->whereNull('system_at');
        }
        $model = $builder->where('status_cancel', 0)
            ->where('buy_user_id', '>', 0)
            ->first([
               /* DB::raw('count(if(status=1,true,null)) as unpaid_order_num'),
                DB::raw('count(if(status=2,true,null)) as undelivered_order_num'),
                DB::raw('count(if(status=3,true,null)) as unconfirmed_order_num'),*/
                DB::raw('count(if(status_after_sale=1,true,null)) as after_sale_order_num')
            ]);
        $proData = $builder->where('status_cancel', 0)
            ->where('buy_user_id', '>', 0)
            ->whereIn('status_after_sale', [0, 3, 4])
            ->first([
                DB::raw('count(if(status=1,true,null)) as unpaid_order_num'),
                DB::raw('count(if(status=2,true,null)) as undelivered_order_num'),
                DB::raw('count(if(status=3,true,null)) as unconfirmed_order_num'),
            ]);
        $proData['after_sale_order_num'] = $model['after_sale_order_num'];
        $auctionOrder = AuctionOrderLogic::query()->where([
            'status_delete' => 0,
            'status_cancel' => 0,
        ]);
        $sale = AfterSaleLogic::query()
            ->leftJoin('auction_order as b', 'b.id', '=', 'object_id')
            ->where([
                'after_sale.status' => 0,
                'object_type' => 'auction_order',
                'status_after_sale' => 1,
                'status_delete' => 0,
                'status_cancel' => 0,
            ]);
        if ($type === 0) {
            $proData['user_action_num'] = $auctionOrder
                ->where([
                    'user_id' => USERID,
                    'user_deleted_at' => null,
                ])
                ->whereIn('status', [2, 3])
                ->whereIn('status_after_sale', [0, 3, 4])
                ->count();
            $proData['sales_user_action_num'] = $sale->where([
                'after_sale.user_id' => USERID,
                'user_deleted_at' => null,
            ])->count();
        } else {
            $proData['seller_action_num'] = $auctionOrder
                ->where([
                    'seller_id' => $userId,
                    'seller_deleted_at' => null,
                ])->whereIn('status', [2])
                ->whereIn('status_after_sale', [0, 3, 4])
                ->count();
            $proData['sales_seller_action_num'] = $sale->where([
                'after_sale.seller_id' => USERID,
                'seller_deleted_at' => null,
            ])->count();
        }
        return $proData;
    }

    /**
     * inviteOrderPage
     * @param InviteOrderPageRequest $request
     * @param array $inviteOrderId
     * @return array
     */
    public static function inviteOrderPage(InviteOrderPageRequest $request, array $inviteOrderId): array
    {
        $builder = self::query()->distinct()
            ->leftJoin('shop_order_ext', 'shop_order.order_id', 'shop_order_ext.order_id')
            ->leftJoin('sales_record', 'sales_record.object_id', 'shop_order.order_id')
            ->leftJoin('shop_user as user', 'user.user_id', 'shop_order.buy_user_id')->with(['ext'])->with([
                'goods' => function ($query) use ($request) {
                    $query->select();
                    if (!is_null($request->keyword)) {
                        $query->orWhere('title', 'like', '%' . $request->keyword . '%')
                            ->orWhere(function ($query) use ($request) {
                                $query->whereJsonContains('info->specInfo->ps_name', $request->keyword);
                            });
                    }
                },
            ])->where([
                'shop_order_ext.status_cancel' => 0,
                'sales_record.type' => 'invite',
            ]);
        if (!is_null($request->status) || $request->status !== 0) {
            if ($request->status === 1) $builder->where(['shop_order_ext.status' => 2, 'status_deliver' => 0]);
            else {//待发货
                if ($request->status === 2) {
                    $builder->where(['shop_order_ext.status_confirm' => 0, 'status_deliver' => 1]);
                } else {//待收货
                    if ($request->status === 3) $builder->where('shop_order_ext.status', 4);
                    else {//已完成
                        if ($request->status === 4) $builder->where('shop_order_ext.status_after_sale', 1);
                        else if ($request->status === 5) $builder->whereIn('sales_record.status', [0, 1]);
                    }
                }
            }
        }
        if (!is_null($request->userId)) $builder->where('from_user_id', $request->userId);
        $builder->whereIn('shop_order.order_id', $inviteOrderId);
        return paginate($builder->paginate($request->limit, [
            'sales_record.id',
            'shop_order.order_id',
            'shop_order.serial_number',
            'shop_order.order_total_real_price',
            'shop_order.pay_price',
            'shop_order.add_at',
            'sales_record.amount',
            'shop_order.order_note',
            'user.user_name',
            'sales_record.status as income_status',
        ]));
    }

    /**
     * customerServicePaging
     * @param BasePageRequest $request
     * @return array
     */
    public function customerServicePaging(BasePageRequest $request): array
    {
        $paginate = self::query()->with(['goodsKefu'])->leftJoin('shop_order_ext', 'shop_order_ext.order_id', 'shop_order.order_id')
            ->where(['buy_user_id' => USERID, 'sell_user_id' => $request->sellerId]);
        if (!is_null($request->keyword)) {
            if (strlen($request->keyword) === 20 && ctype_digit($request->keyword)) {
                $paginate->where('shop_order.serial_number', $request->keyword);
            } else {
                $paginate->leftJoin('shop_order_goods', 'shop_order_goods.order_id', 'shop_order.order_id')
                    ->where(function (Builder $builder) use ($request) {
                        $builder->where(function (Builder $builder) use ($request) {
                            $builder->where('title', 'like', '%' . $request->keyword . '%');
                        })->orWhere(function (Builder $builder) use ($request) {
                            $builder->where('spec', $request->keyword);
                        });
                    });
            }
        }
        $paginate = $paginate->whereNotIn('shop_order_ext.status', [0, 1])->orderByDesc('shop_order.created_at')
            ->paginate($request->limit, ['shop_order.serial_number', 'shop_order_ext.status', 'shop_order.order_id']);
        return paginate($paginate);
    }

    /**
     * plugAnnounce
     * @param BasePageRequest $request
     * @return LengthAwarePaginator
     */
    public function plugAnnounce(BasePageRequest $request): LengthAwarePaginator
    {
        return self::query()->leftJoin('shop_order_goods', 'shop_order_goods.order_id', 'shop_order.order_id')
            ->leftJoin('shop_order_ext', 'shop_order_ext.order_id', 'shop_order.order_id')
            ->leftJoin('shop_user', 'shop_user.user_id', 'shop_order_ext.user_id')->where([
                'shop_order.plug' => 1, 'shop_order_ext.status_pay' => 1,
            ])->whereBetween('shop_order.created_at', [
                now()->startOfDay()->subDays()->toDateTimeString(),
                now()->endOfDay()->subDays()->toDateTimeString(),
            ])->orderByDesc('shop_order.created_at')->paginate($request->limit, [
                'pay_price',
                'shop_order_goods.product_file_id',
                'shop_order_goods.spec_file_id',
                'shop_order_goods.title',
                'shop_order_goods.spec',
                'shop_order_ext.extend',
                'shop_order_ext.user_id',
                'shop_user.user_mobile as mobile',
                'shop_order.created_at',
            ]);
    }

    /**
     * hColumns
     * @return array
     */
    private function hColumns(): array
    {
        return [
            'shop_order.order_id',
            'shop_order.serial_number',
            'order_sn',
            'shop_order.sell_user_id',
            'shop_order.product_id',
            'order_total_price',
            'order_total_handleing_price',
            'order_ensure_price',
            'order_total_real_price',
            'order_is_self_pickup',
            DB::raw("from_unixtime(order_add_time, '%Y-%m-%d %H:%i:%s') as createdAt"),
            'is_cart',
            'shop_order.plug',
            'shop_order.pay_price',
            'shop_order.coupon_discount',
            'shop_order.help',
        ];
    }

    /**
     * hKeyword
     * @param BasePageRequest $request
     * @param Builder $builder
     * @return void
     */
    private function hKeyword(BasePageRequest $request, Builder $builder): void
    {
        if (!is_null($request->keyword)) {
            $builder->leftJoin('shop_order_goods', 'shop_order_goods.order_id', 'shop_order.order_id')
                ->where(function ($query) use ($request) {
                    $query->where('shop_order_goods.title', 'like', "%{$request->keyword}%")
                        ->orWhere('shop_order.serial_number', 'like', "%{$request->keyword}%");
                });
            $request->offsetUnset('keyword');
        }
    }
}
